home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
DDJMAG
/
DDJ9110.ZIP
/
OODEBUGR.ASC
< prev
next >
Wrap
Text File
|
1991-09-10
|
8KB
|
244 lines
_A MINIMAL OBJECT-ORIENTED DEBUGGER FOR C++_
by William M. Miller
[LISTING ONE]
// MOOD.hxx by William M. Miller, 8/3/91. MOOD user declarations.
/* This header file is included in every module which is to be metered
* by MOOD. Its actions are controlled by the preprocessor variable
* DEBUGGER_ON. If not this is not defined, no debugger actions will occur.
*/
#ifndef _DEBUGGER_DEFS
#define _DEBUGGER_DEFS
// Conditions under which cond_break may be called:
enum break_condition {
PROG_START, PROG_END, FCN_ENTRY, FCN_EXIT, OBJ_CTOR, OBJ_DTOR,
USER_BP
};
/* The cond_break() function is called in all the above contexts for two
* purposes: 1) to display trace information when verbose mode is on, and
* 2) to break under the appropriate conditions to allow the user to
* interact with the debugger. The default arguments are to allow a user
* program to contain the call "cond_break();" with no arguments to perform
* an unconditional breakpoint into the debugger's interactive mode.
*/
void cond_break(break_condition cond = USER_BP, const char* name = 0,
void* addr = 0);
/* The trace class is intended to allow function tracing. Each function or
* block which should be included in the trace should declare an object of
* class trace at the very beginning. The result, in verbose mode, will be
* to display the function/block name at entry and exit; this name can also
* be used to set a breakpoint from the debugger's interactive mode.
*/
class trace {
public:
#ifdef DEBUGGER_ON
trace( const char* fcn_name): _name(fcn_name)
{ cond_break( FCN_ENTRY, _name); }
~trace( ) { cond_break( FCN_EXIT, _name); }
private:
const char* _name;
#else
trace( const char* ) { }
#endif
};
/* The monitored class is intended for use as a virtual base class of any
* classes whose construction/destruction is to be traced in verbose mode
* or whose values are to be displayed interactively. Derived classes must
* pass the object or class name to the constructor and must supply an
* override to the display() member function.
*/
class monitored {
public:
#ifdef DEBUGGER_ON
monitored( const char* obj_name ): _name(obj_name)
{ cond_break(OBJ_CTOR, _name, this); };
~monitored( ) { cond_break(OBJ_DTOR, _name, this); }
virtual void display() = 0;
private:
monitored() { } // keep cfront 2.0 happy -- it requires a default
// constructor in virtual base classes for no good reason.
const char* _name;
#else
monitored( const char* ) { }
monitored( ) { }
#endif
};
/* The following class and static object call cond_break exactly once at
* program start, before any debuggable object is created, to allow the
* user to set up tracing and breakpoints, and once at the end of execution
* to allow for any needed cleanup.
*/
#ifdef DEBUGGER_ON
class init_ctl {
public:
init_ctl( ) { if (_count++ == 0) cond_break(PROG_START); }
~init_ctl( ) { if (--_count == 0) cond_break(PROG_END); }
private:
static int _count;
};
static init_ctl dbg_init;
#endif
#endif
[LISTING TWO]
// tdbg.cxx by William M. Miller, 8/3/91. This is a sample
// program to be debugged with MOOD. A transcript of the debugging
// session is shown in Example 3, accompanying this article.
extern "C" {
#include <stdio.h>
}
#include "MOOD.hxx"
struct foo: virtual monitored {
foo( const char* nm ): monitored(nm), my_name(nm) { }
void display( ) { fprintf(stderr, "%s\n", my_name); }
const char* my_name;
};
void x();
void y();
void z();
int main() {
trace tt("main");
foo* p = new foo("*p");
x();
z();
delete p;
return 0;
}
void x() {
trace tt("x");
foo xf("xf");
y();
}
void y() {
trace tt("y");
foo yf("yf");
}
void z() {
trace tt("z");
foo zf("zf");
}
[LISTING THREE]
// MOOD.cxx, by William M. Miller, 8/3/91. MOOD kernel definitions.
/* This routine implements the user interface of MOOD, the Minimal Object
* Oriented Debugger. Example 2 in the accompanying article describes
* currently available commands: s, g, d, v, and q.
*/
extern "C" {
#include <stdio.h>
#include <string.h>
}
#define DEBUGGER_ON 1
#include "MOOD.hxx"
/* The objp() function does a system-dependent conversion of an ASCII pointer
* specification into a pointer to a monitored object.
*/
monitored* objp(const char* str);
/* The cond_break() function is called under the various circumstances
* described by the enumeration break_condition. It prints a message, if
* required, describing the reason for its call, and optionally enters
* interactive mode to take commands.
*/
void cond_break(break_condition cond, const char* name, void* addr) {
static int tracing = 0; // => verbose mode
static char brk_name[128] = ""; // name on which to break
static int was_step = 1; // last cmd was "step" => go to
// interactive mode
char buff[128]; // command line buffer
/* We enter the display and possible interactive mode code under the following
* conditions:
* 1) We are tracing (verbose mode).
* 2) We are stepping. (Note: this is initially TRUE, which takes care of
* getting into interactive mode on the PROG_START call.)
* 3) The breakpoint name was set and the current name matches it, or this
* is a user breakpoint call with no name
* 4) The breakpoint name was not set and this is a user breakpoint call.
*/
if (tracing || was_step ||
(brk_name[0] && ((strcmp(brk_name, name) == 0) ||
(cond == USER_BP && !name))) || (!brk_name[0] && cond == USER_BP)) {
switch(cond) { // Print an appropriate message:
case PROG_START:
fprintf(stderr,"MOOD: Minimal Object Oriented Debugger, V. 0.0\n");
break;
case PROG_END: fprintf(stderr, "End of execution.\n"); break;
case FCN_ENTRY: fprintf(stderr, "Enter %s\n", name); break;
case FCN_EXIT: fprintf(stderr, "Exit %s\n", name); break;
case OBJ_CTOR: fprintf(stderr, "Construct %s @ %p\n", name, addr);
break;
case OBJ_DTOR: fprintf(stderr, "Destruct %s @ %p\n", name, addr);
break;
case USER_BP: fprintf(stderr, "Breakpoint %s (%p)\n", name, addr);
break;
} // switch
/* We enter interactive mode if any of the above conditions other than
* tracing is met. (This implies that named user breakpoints are skipped
* if the user uses a g <name> command in which the name does not match
* the breakpoint name, but that unnamed user breakpoints are always
* effective, as are named user breakpoints after a g command with no name.)
*/
if (was_step || (brk_name[0] && ((strcmp(brk_name, name) == 0) ||
(cond == USER_BP && !name))) ||
(!brk_name[0] && cond == USER_BP)) {
// Reset breakpoint conditions
was_step = 0;
brk_name[0] = 0;
// Main command loop
do {
fprintf(stderr, "cmd> ");
gets(buff);
switch(buff[0]) {
case 'd': objp(buff + 2)->display(); break;
case 'g': if (buff[1]) strcpy(brk_name, buff + 2); break;
case 'q': tracing = 0; break;
case 's': was_step = 1; break;
case 'v': tracing = 1; break;
} // switch
} while(buff[0] != 's' && buff[0] != 'g');
} // if (interactive)
} // if (message)
} // cond_break()
monitored* objp(const char* str) {
monitored* p;
sscanf(str, "%p", &p);
return p;
}
int init_ctl::_count;